558. Червячные дыры
Червячная дыра – это
односторонний тоннель, соединяющий две звездные системы. Время движения по
тоннелю мгновенно, но при этом можно попасть либо в будущее, либо в прошлое на
определенное число лет. Тоннели соединяют только разные звездные системы.
Известно, что из Солнечной системы можно попасть в любую другую звездную
систему. В задаче необходимо установить, можно ли землянину, двигаясь по
червячным дырам, попасть в бесконечное прошлое.
Вход. Первая строка содержит количество тестов c.
Первая строка каждого теста содержит число звездных систем n (1 £ n £ 1000) и число червячных дыр m (0 £ m £ 2000). Звездные системы нумеруются числами от 0 до n – 1
(Солнечная система имеет номер 0). Следующие m строк содержат три числа x,
y, t (-1000 £ t
£ 1000). При перемещении со звездной системы x в
систему y происходит перемещение во времени на t лет. Если t
> 0, то перемещение происходит в будущее, если t < 0 – то в
прошлое.
Выход. Для
каждого теста вывести сообщение “possible” или “not possible” в зависимости от
возможности ил невозможности попасть в бесконечное прошлое.
2
3 3
0 1 1000
1 2 15
2 1 -42
4 4
0 1 10
1 2 20
2 3 30
3 0 -60
possible
not possible
графы, циклы отрицательной длины, алгоритм Беллмана - Форда
Если в графе существует
цикл отрицательной длины, то двигаясь по нем можно попасть в бесконечное
прошлое. Для проверки существования такого цикла следует воспользоваться
алгоритмом Беллмана – Форда.
Обозначим через d[v] верхнюю оценку веса
кратчайшего пути из начальной вершины s в вершину v.
Пусть w(u, v) – вес ребра (u, v). Релаксацией ребра
(u, v) называется уменьшение значения d[v] до d[u]
+ w(u, v) (если второе значение меньше первого).
Relax(u, v, w)
if (d[v] > d[u] + w(u, v))
then d[v] = d[u] + w(u, v);
Изначально следует положить
значения d[v] для всех v Î V(G) \ {s} равными плюс
бесконечности, а d[s] = 0. Это совершается в процедуре инициализации.
инициализация (G, s)
for всех вершин v Î V(G) do d[v] = ¥;
d[s] = 0;
Функция Bellman_Ford совершает релаксацию всех ребер |V(G)| – 1 раз. Если в графе присутствует цикл отрицательной длины, то
по завершению предыдущей операции существует ребро (u, v),
для которого выполняется неравенство d[v] > d[u] + w(u, v). Функция Bellman_Ford возвращает FALSE, если
найден цикл отрицательной длины и TRUE иначе.
Bellman_Ford(G, w, s)
иницализация (G, s)
for i = 1 to |V(G)|
– 1 do
for каждого ребра (u, v) Î E(G) do Relax(u, v, w);
for каждого ребра (u, v) Î E(G) do
if d[v] > d[u] + w(u,
v) then return FALSE;
return TRUE;
Поскольку землянин стартует
с Солнечной системы, то начальной будет вершина с номером 0, то есть s =
0.
Лемма. Пусть d(s, v) –
длина кратчайшего пути от s до v. Пусть G(V, E) – взвешенный
ориентированный граф с весовой функцией w, не содержащий циклов
отрицательного веса, достижимых из s. Тогда по окончании работы
процедуры Bellman_Ford будет выполняться
равенство d[v] = d(s, v) для
всех вершин v, достижимых из s.
Лемма. Если в графе существует
цикл отрицательной длины, то существует ребро (u, v), для которого выполняется
неравенство d[v]
> d[u] + w(u, v).
Доказательство. Пусть (v0,
v1, …, vk = v0) – цикл
отрицательной длины, достижимый из исходной вершины, но при этом d[vi+1]
£ d[vi] +
w(vi, vi+1) для всех i =
0, 1, …, k – 1. Сложив эти k неравенств, и сократив на , получим: 0 £ . Последнее неравенство противоречит тому что (v0,
v1, …, vk = v0) является
циклом отрицательной длины. Сокращаемая сумма конечна, так как все
вершины vi достижимы.
Рассмотрим первый
пример. Граф содержит 3 вершины. Проводим два раза релаксацию всех ребер.
начальное состояние граф после первой
релаксации всех ребер
граф после второй
релаксации всех ребер
Для ребра (2, 1) имеет
место неравенство: d[1] > d[2] + w(2, 1) (1000 > 1015 – 42). Значит
существует цикл отрицательной длины, в который входит ребро (2, 1). Таким
циклом будет 1 – 2 – 1 и его величина равна 15 – 42 = -27.
Во втором тесте циклов
отрицательной длины нет.
Каждая червячная дыра задается своими концами и
временем, на которое она перемещает путника. Номера звездных систем,
являющимися концами i – ой дыры, храним в ячейках массивов x[i]
и y[i], величину перемещения во времени – в w[i].
int x[2001],
y[2001], w[2001];
Функция Bellman_Ford возвращает 0, если в исследуемом
графе есть хотя бы один цикл отрицательной длины, и 1 в противном случае.
Для каждой вершины v значение d[v]
хранит верхнюю оценку веса кратчайшего пути из начальной вершины в вершину v.
Начальной является нулевая вершина, соответствующая Солнечной системе. Положим
в начале работы алгоритма d[0] равным 0, а d[v] равным некоторому
максимально возможному числу (например, 0x3F3F3F3F).
int Bellman_Ford(void)
{
int d[1001], i, j;
memset(d,0x3F,sizeof(d));
d[0] = 0;
Повторим n (по числу вершин в графе) раз
следующую процедуру: для каждого ребра графа произвести его релаксацию. Для
избежания переполнения при сложении d[x[j]] + w[j] следует
удостовериться, что значение d[x[j]] меньше максимального числа.
for(i = 0; i < n; i++)
for (j = 0; j < m; j++)
if (d[x[j]] < 0x3F3F3F3F)
if (d[y[j]] > d[x[j]] + w[j])
d[y[j]] = d[x[j]] + w[j];
Проверим, существует
ли цикл отрицательной длины. Перебираем все ребра (x[j], y[j]), 0
£ j
< m. Если для некоторого j - го ребра выполняется условие d[y[j]]
> d[x[j]] + w[j], то обнаружен цикл отрицательной длины.
Возвращаем 0 и выходим из функции.
for(j = 0; j < m; j++)
if (d[y[j]]
> d[x[j]] + w[j]) return 0;
Циклов отрицательной длины не найдено. Возвращаем 1.
return 1;
}
Основная процедура. Читаем число тестов k. Для
каждого теста вводим число звездных систем n и число червячных дыр m.
Далее читаем информацию о тоннелях в массивы x, y, w.
scanf("%d",&k);
while (k--)
{
scanf("%d %d",&n,&m);
for(i = 0; i < m; i++)
scanf("%d
%d %d",&x[i],&y[i],&w[i]);
Запускаем функцию Bellman_Ford. В зависимости от ее
результата выводим информацию о возможности или невозможности переместиться в
минус бесконечность.
if (Bellman_Ford()) printf("not
");
printf("possible\n");
}